Programming Ecto by Darin Wilson & Eric Meadows-Jonsson

Programming Ecto by Darin Wilson & Eric Meadows-Jonsson

Author:Darin Wilson & Eric Meadows-Jonsson [Wilson, Darin]
Language: eng
Format: epub
Tags: Pragmatic Bookshelf
Publisher: Pragmatic Bookshelf
Published: 2019-03-31T22:00:00+00:00


Examining the List of Changes So Far

The last value of the returned tuple is supposed to be a list of changes that occurred before the error happened. Let’s take another look at what we got in the last example:

artist = Repo.get_by(Artist, name: "Johnny Hodges")

artist_changeset = Artist.changeset(artist,

%{name: "John Cornelius Hodges"})

invalid_changeset = Artist.changeset(%Artist{},

%{name: nil})

multi =

Multi.new

|> Multi.update(:artist, artist_changeset)

|> Multi.insert(:invalid, invalid_changeset)

Repo.transaction(multi)

#=> {:error, :invalid,

#=> #Ecto.Changeset<

#=> action: :insert,

#=> changes: %{},

#=> errors: [name: {"can't be blank", [validation: :required]}],

#=> data: #MusicDB.Artist<>,

#=> valid?: false

#=> >, %{}}

We got an empty map—that seems surprising. The return value told us that the second operation in the Multi failed, so we would expect to see the result of the first operation in the list of changes so far.

This is because Ecto doesn’t like to waste the database’s time. If the Multi contains operations that use changesets, Ecto first checks to make sure all the changesets are valid. If any are not, Ecto won’t bother running the transaction at all. It just flags the invalid changeset and sends it back to us in the return value. There’s no need to trouble the database with an invalid changeset.

Let’s try a different example so we can see something besides an empty map. We’ll create a new Multi that starts with a successful update. We’ll then force an error by trying to insert a new %Genre{} record with a name that already exists in the database (as you might recall from Working with Constraints, the genres table has a unique index on the name column).

artist = Repo.get_by(Artist, name: "Johnny Hodges")

artist_changeset = Artist.changeset(artist,

%{name: "John Cornelius Hodges"})

genre_changeset =

%Genre{}

|> Ecto.Changeset.cast(%{name: "jazz"}, [:name])

|> Ecto.Changeset.unique_constraint(:name)

multi =

Multi.new

|> Multi.update(:artist, artist_changeset)

|> Multi.insert(:bad_genre, genre_changeset)

Repo.transaction(multi)

#=> {:error, :bad_genre, #Ecto.Changeset< ... >,

#=> %{

#=> artist: %MusicDB.Artist{

#=> __meta__: #Ecto.Schema.Metadata<:loaded, "artists">,

#=> albums: #Ecto.Association.NotLoaded<association

#=> :albums is not loaded>,

#=> birth_date: nil,

#=> death_date: nil,

#=> id: 4,

#=> inserted_at: ~N[2018-03-23 14:02:28],

#=> name: "John Cornelius Hodges",

#=> tracks: #Ecto.Association.NotLoaded<association

#=> :tracks is not loaded>,

#=> updated_at: ~N[2018-03-23 14:02:28]

#=> }

#=> }}

Now we can get a good look at that last value. The keys in the map correspond to our named Multi functions that have already been run. In this example, we just had the one :artist update so that’s all this map contains. The value of the item is the result of the operation. Here we can see that our “Johnny Hodges” record was updated to “John Cornelius Hodges” as we expected. But because the Multi failed (thanks to the addition of our bad_genre operation), the database rolled back the change. We can confirm that by looking at the database again:

Repo.get_by(Artist, name: "John Cornelius Hodges")

#=> nil

We get no records back when we search for “John Cornelius Hodges,” which confirms that our update was indeed rolled back.



Download



Copyright Disclaimer:
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.